package com.gitee.qdbp.jdbc.plugins;

import java.math.BigDecimal;
import java.sql.Date;
import com.gitee.qdbp.jdbc.model.MainDbType;
import com.gitee.qdbp.jdbc.plugins.impl.BatchInsertByMultiRowsExecutor;
import com.gitee.qdbp.jdbc.plugins.impl.BatchOperateByForEachExecutor;
import com.gitee.qdbp.jdbc.plugins.impl.BatchOperateByMultiSqlExecutor;
import com.gitee.qdbp.jdbc.plugins.impl.BatchUpdateByCaseWhenExecutor;
import com.gitee.qdbp.jdbc.plugins.impl.BatchUpdateByJoinUsingExecutor;
import com.gitee.qdbp.jdbc.plugins.impl.ConfigableJdbcDataTypeResolver;
import com.gitee.qdbp.jdbc.plugins.impl.DataSourceDbVersionFinder;
import com.gitee.qdbp.jdbc.plugins.impl.FastJsonBeanToMapConverter;
import com.gitee.qdbp.jdbc.plugins.impl.FastJsonDbConditionConverter;
import com.gitee.qdbp.jdbc.plugins.impl.NoneEntityDataStateFillStrategy;
import com.gitee.qdbp.jdbc.plugins.impl.OrderByFunctionSqlBuilder;
import com.gitee.qdbp.jdbc.plugins.impl.PersistenceAnnotationTableScans;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleColumnValueValidator;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleDbOperatorContainer;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleEntityFieldFillStrategy;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleJdbcNamingConverter;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleRawValueConverter;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlDialect;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFormatter;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFragmentOptions;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlReplacer;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleTablesFieldColumnParser;
import com.gitee.qdbp.jdbc.plugins.impl.SimpleVarToDbValueConverter;
import com.gitee.qdbp.jdbc.plugins.impl.SpringMapToBeanConverter;
import com.gitee.qdbp.jdbc.plugins.impl.SpringSqlFileScanner;
import com.gitee.qdbp.jdbc.plugins.impl.SpringTypeConverter;
import com.gitee.qdbp.jdbc.result.CamelNamingRowToMapMapper;
import com.gitee.qdbp.jdbc.result.TableRowToBeanMapper;
import com.gitee.qdbp.jdbc.result.TablesRowToProperyMapper;
import com.gitee.qdbp.jdbc.support.convert.NumberToBooleanConverter;
import com.gitee.qdbp.jdbc.support.convert.StringToDateConverter;
import com.gitee.qdbp.jdbc.support.enums.AllEnumConverterRegister;
import com.gitee.qdbp.tools.utils.Config;
import com.gitee.qdbp.tools.utils.ReflectTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.DefaultConversionService;

/**
 * 插件初始化工具类
 *
 * @author zhaohuihua
 * @version 20210704
 */
public class DbPluginInitTools {

    private static final Logger log = LoggerFactory.getLogger(DbPluginInitTools.class);

    public static void initDefaultConverter(ConversionService conversionService) {
        if (conversionService instanceof ConverterRegistry) {
            ConverterRegistry registry = (ConverterRegistry) conversionService;
            if (!conversionService.canConvert(String.class, Date.class)) {
                registry.addConverter(new StringToDateConverter());
            }
            // oracle, 如果数字字段定义的类型是SMALLINT, 将会返回BigDecimal
            if (!conversionService.canConvert(BigDecimal.class, Boolean.class)) {
                registry.addConverter(new NumberToBooleanConverter());
            }
            // 注册其他枚举值转换处理类: oracle的BigDecimal转Enum等
            AllEnumConverterRegister.registerEnumConverterFactory(registry);

            try {
                // 注册日期转换处理类
                // DateConverter里面都是jdk8的转换处理类, qdbc默认支持jdk7, 因此使用反射调用
                Class<?> dateConverter = Class.forName("com.gitee.qdbp.jdbc.support.convert.DateConverter");
                ReflectTools.invokeMethod(dateConverter, "registerConverters", conversionService);
            } catch (ClassNotFoundException ignore) {
            } catch (Exception e) {
                log.warn("Failed to invoke method of DateConverter.registerConverters()", e);
            }
        }
    }

    /**
     * 检查并设置默认属性<br>
     * <br>
     * 灵活就意味着可配置的细节多, 也就难以上手<br>
     * 提供一个默认版本, 用于满足基本需求, 基于约定,常用,无定制<br>
     * <br>
     * 未提供的特性包括:<br>
     * TableInfoScans.commonFieldResolver: 不会将公共字段放在查询列表的最后<br>
     * EntityFieldFillStrategy.entityFillBizResolver: 不会自动填充创建人/创建时间等业务参数<br>
     * EntityDataStateFillStrategy: 不会自动填充数据状态, 不支持逻辑删除<br>
     * 等等...
     * 
     * @param plugins 插件容器
     */
    public static void checkAndSetDefaultProperty(DbPluginContainer plugins) {
        if (plugins.getSqlConfig() == null) {
            Config config = new Config();
            config.put("recursive.keyword", "WITH RECURSIVE");
            config.put("recursive.keyword.db2", "WITH");
            config.put("recursive.keyword.sqlserver", "WITH");
            plugins.setSqlConfig(config);
        }
        if (plugins.getConversionService() == null) {
            plugins.setConversionService(DefaultConversionService.getSharedInstance());
        }
        if (plugins.getNamingConverter() == null) {
            plugins.setNamingConverter(new SimpleJdbcNamingConverter());
        }
        if (plugins.getAvailableDbTypes() == null) {
            plugins.addAvailableDbTypeClass(MainDbType.class);
        }
        if (plugins.getSqlTaglibCreator() == null) {
            plugins.setSqlTaglibPath("classpath:settings/qdbc/qdbc.taglib.txt");
        }
        if (plugins.getJdbcDataTypeResolver() == null) {
            String path = "classpath:settings/qdbc/qdbc.datatype.txt";
            plugins.setJdbcDataTypeResolver(new ConfigableJdbcDataTypeResolver(path));
        }
        if (plugins.getTableInfoScans() == null) {
            plugins.setTableInfoScans(new PersistenceAnnotationTableScans());
        }
        if (plugins.getTablesFieldColumnParser() == null) {
            plugins.setTablesFieldColumnParser(new SimpleTablesFieldColumnParser());
        }
        if (plugins.getEntityFieldFillStrategy() == null) {
            plugins.setEntityFieldFillStrategy(new SimpleEntityFieldFillStrategy());
        }
        if (plugins.getEntityDataStateFillStrategy() == null) {
            plugins.setEntityDataStateFillStrategy(new NoneEntityDataStateFillStrategy());
        }
        if (plugins.getRawValueConverter() == null) {
            plugins.setRawValueConverter(new SimpleRawValueConverter());
        }
        if (plugins.getToDbValueConverter() == null) {
            plugins.setToDbValueConverter(new SimpleVarToDbValueConverter());
        }
        if (plugins.getObjectTypeConverter() == null) {
            plugins.setObjectTypeConverter(new SpringTypeConverter());
        }
        if (plugins.getRowToMapConverter() == null) {
            CamelNamingRowToMapMapper mapper = new CamelNamingRowToMapMapper();
            mapper.setPlugins(plugins.helper());
            plugins.setRowToMapConverter(mapper);
        }
        if (plugins.getTableRowToBeanFactory() == null) {
            plugins.setTableRowToBeanFactory(new TableRowToBeanMapper.Factory());
        }
        if (plugins.getTablesRowToBeanFactory() == null) {
            plugins.setTablesRowToBeanFactory(new TablesRowToProperyMapper.Factory());
        }
        if (plugins.getMapToBeanConverter() == null) {
            // 由于fastjson的TypeUtils.castToEnum()逻辑存在硬伤, 无法做到数字枚举值的自定义转换
            // container.setMapToBeanConverter(new FastJsonMapToBeanConverter());
            // 改为SpringMapToBeanConverter
            plugins.setMapToBeanConverter(new SpringMapToBeanConverter());
        }
        if (plugins.getBeanToMapConverter() == null) {
            plugins.setBeanToMapConverter(new FastJsonBeanToMapConverter());
        }
        if (plugins.getDbConditionConverter() == null) {
            plugins.setDbConditionConverter(new FastJsonDbConditionConverter());
        }
        if (plugins.getOperatorContainer() == null) {
            plugins.setOperatorContainer(new SimpleDbOperatorContainer());
        }
        if (plugins.getSqlFormatter() == null) {
            plugins.setSqlFormatter(new SimpleSqlFormatter());
        }
        if (plugins.getSqlReplacer() == null) {
            plugins.setSqlReplacer(new SimpleSqlReplacer());
        }
        if (plugins.getDbVersionFinder() == null) {
            plugins.setDbVersionFinder(new DataSourceDbVersionFinder());
        }
        if (plugins.getSqlDialectCreator() == null) {
            plugins.setSqlDialectCreator(new SimpleSqlDialect.Creator());
        }
        if (plugins.getSqlFileScanner() == null) {
            plugins.setSqlFileScanner(new SpringSqlFileScanner());
        }
        if (plugins.getSqlFragmentOptions() == null) {
            plugins.setSqlFragmentOptions(new SimpleSqlFragmentOptions());
        }
        if (plugins.getColumnValueValidator() == null) {
            plugins.setColumnValueValidator(new SimpleColumnValueValidator());
        }
        if (plugins.getOrderBySqlBuilders().isEmpty()) {
            plugins.addOrderBySqlBuilder(new OrderByFunctionSqlBuilder());
        }
        if (plugins.getDefaultBatchInsertExecutor() == null) {
            plugins.setDefaultBatchInsertExecutor(new BatchOperateByForEachExecutor());
        }
        if (plugins.getDefaultBatchUpdateExecutor() == null) {
            plugins.setDefaultBatchUpdateExecutor(new BatchOperateByForEachExecutor());
        }
        // 初始化公共的批量操作处理器(通用的放前面,专用的放后面)
        if (plugins.getBatchInsertExecutors().isEmpty()) {
            plugins.addBatchInsertExecutor(new BatchOperateByMultiSqlExecutor()); // mysql,oracle
            plugins.addBatchInsertExecutor(new BatchInsertByMultiRowsExecutor()); // mysql,db2

            // BatchInsertByUnionAllFromDualExecutor有问题
            // Oracle的CLOB字段批量新增时, 如果CLOB字段值既有小于4000的也有大于4000的
            // 就会报错ORA-01790: 表达式必须具有与对应表达式相同的数据类型
            // 原因是JdbcTemplate对CLOB的处理, 小于4000的用ps.setString(), 超过4000的用ps.setClob()
            // 详见StatementCreatorUtils.setValue()
            // plugins.addBatchInsertExecutor(new BatchInsertByUnionAllFromDualExecutor());
        }
        if (plugins.getBatchUpdateExecutors().isEmpty()) {
            plugins.addBatchInsertExecutor(new BatchOperateByMultiSqlExecutor()); // mysql,oracle
            plugins.addBatchUpdateExecutor(new BatchUpdateByCaseWhenExecutor()); // mysql,db2
            plugins.addBatchUpdateExecutor(new BatchUpdateByJoinUsingExecutor()); // mysql
        }
    }
}
